
#include "RtBase.h"
#include "RtDnsManager.h"
#include "RtThreadManager.h"
#include "RtObserver.h"
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
#include "RtThreadReactor.h"
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

//////////////////////////////////////////////////////////////////////
// class CRtDnsManager
//////////////////////////////////////////////////////////////////////

#define WIN32_DNS_CLASS_NAME "RtWin32DNSNotification"
#define WM_WIN32_DNS_EVENT WM_USER+133

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
ATOM g_atomDnsRegisterClass;
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

CRtDnsManager::CRtDnsManager()
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	: m_hwndDsnWindow(NULL)
	, m_bIsEL98(FALSE)
	,
#else
	: 
#endif
	m_pThreadDNS(NULL)

//#endif // RT_WIN32_ENABLE_AsyncGetHostByName
{
	m_pThreadNetwork = CRtThreadManager::Instance()->GetThread(
		CRtThreadManager::TT_NETWORK);
	RT_ASSERTE(m_pThreadNetwork);

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	BOOL bOsVersionInfoEx;
	OSVERSIONINFOEX osvi;
	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
	{
		RT_WARNING_TRACE_THIS("CRtDnsManager::CRtDnsManager() GetVersionEx failed, err = " << ::GetLastError());
		osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
		if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
		{
			RT_WARNING_TRACE_THIS("CRtDnsManager::CRtDnsManager() GetVersionEx failed, err = " << ::GetLastError());
		}
	}

	if (VER_PLATFORM_WIN32_WINDOWS == osvi.dwPlatformId)
	{
		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion <= 10)
		{
			RT_INFO_TRACE_THIS("CRtDnsManager::CRtDnsManager() is win98 or lower!");
			m_bIsEL98 = TRUE;
		}
	}
	// we must create window in network thread.
	if(!m_bIsEL98)
	{
	RtResult rv;
	if (CRtThreadManager::IsEqualCurrentThread(m_pThreadNetwork->GetThreadId())) {
		rv = CreateDnsWindow();
	}
	else {
		// we have to use SendEvent() to wait CreateDnsWindow() finished.
		rv = m_pThreadNetwork->GetEventQueue()->SendEvent(this);
	}
	RT_ASSERTE(RT_SUCCEEDED(rv));
	}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

	// schedule timer in the main thread that cooperating with ~CRtDnsManager().
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(CRtThreadManager::TT_MAIN));
	RT_INFO_TRACE_THIS("CRtDnsManager::CRtDnsManager");
	m_TimerExpired.Schedule(this, CRtTimeValue(3, 0));
}

CRtDnsManager::~CRtDnsManager()
{
	Shutdown();
}

CRtDnsManager* CRtDnsManager::Instance()
{
	return CRtSingletonT<CRtDnsManager>::Instance();
}

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
RtResult CRtDnsManager::CreateDnsWindow()
{
	RT_ASSERTE(!m_bIsEL98);
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	RT_ASSERTE_RETURN(!m_hwndDsnWindow, RT_ERROR_ALREADY_INITIALIZED);

	if (g_atomDnsRegisterClass == 0) {
		WNDCLASS wc;
		wc.style = 0;
		wc.lpfnWndProc = DnsEventWndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = sizeof(void*);
		wc.hInstance = NULL;
		wc.hIcon = 0;
		wc.hCursor = 0;
		wc.hbrBackground = 0;
		wc.lpszMenuName = NULL;
		wc.lpszClassName = WIN32_DNS_CLASS_NAME;
		if ((g_atomDnsRegisterClass = ::RegisterClass(&wc)) == 0) {
			RT_ERROR_TRACE("CRtDnsManager::CreateDnsWindow, RegisterClass() failed!"
					" err=" << ::GetLastError());
			goto fail;
		}
	}

	m_hwndDsnWindow = ::CreateWindow(WIN32_DNS_CLASS_NAME, NULL, 
		WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, NULL, 0);
	if (!m_hwndDsnWindow) {
		RT_ERROR_TRACE("CRtDnsManager::CreateDnsWindow, CreateWindow() failed!"
			" err=" << ::GetLastError());
		goto fail;
	}

	::SetLastError(0);
	if (::SetWindowLong(m_hwndDsnWindow, 0, (LONG)this) == 0  && 
		::GetLastError() != 0) 
	{
		RT_ERROR_TRACE("CRtDnsManager::CreateDnsWindow, SetWindowLong() failed!"
			" err=" << ::GetLastError());
		goto fail;
	}
	
	// MSDN: A thread cannot use DestroyWindow to destroy a window created by a different thread. 
	// That's why we register DnsManager to the network thread. 
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadNetwork->GetThreadId()));
	((CRtThreadReactor*)m_pThreadNetwork)->RegisterDnsManager(this);
	return RT_OK;
	
fail:
	if (m_hwndDsnWindow) {
		::DestroyWindow(m_hwndDsnWindow);
		m_hwndDsnWindow = NULL;
	}
	return RT_ERROR_UNEXPECTED;
}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

RtResult CRtDnsManager::
AsyncResolve(CRtDnsRecord *&aRecord, const CRtString &aHostName, 
			 IRtObserver *aObserver, BOOL aBypassCache, 
			 CRtThread *aThreadListener)
{
	RT_ASSERTE(!aRecord);
	RT_INFO_TRACE("CRtDnsManager::AsyncResolve,"
		" aHostName=" << aHostName << 
		" aObserver=" << aObserver << 
		" aBypassCache=" << aBypassCache << 
		" aThreadListener=" << aThreadListener);

	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	if (!aBypassCache) {
		RtResult rv = FindInCache_l(aRecord, aHostName);
		if (RT_SUCCEEDED(rv))
			return rv;
	}
	CRtAutoPtr<CRtDnsRecord> pRecordNew = new CRtDnsRecord(aHostName
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
		, m_bIsEL98
#endif
	);
	int nErr = BeginResolve_l(pRecordNew.ParaIn());
	if (nErr) {
		Resolved_l(pRecordNew.ParaIn(), nErr, FALSE);
		return RT_ERROR_FAILURE;
	}

	TryAddObserver_l(aObserver, aThreadListener, aHostName);
	return RT_ERROR_WOULD_BLOCK;
}

RtResult CRtDnsManager::
SyncResolve(CRtDnsRecord *&aRecord, const CRtString &aHostName, BOOL aBypassCache)
{
	RT_ASSERTE(!aRecord);
	RT_INFO_TRACE("CRtDnsManager::SyncResolve,"
		" aHostName=" << aHostName << 
		" aBypassCache=" << aBypassCache);

	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	if (!aBypassCache) {
		RtResult rv = FindInCache_l(aRecord, aHostName);
		if (RT_SUCCEEDED(rv))
			return rv;
	}

	CRtAutoPtr<CRtDnsRecord> pRecordNew;
	PendingRecordsType::iterator iterPending = m_PendingRecords.begin();
	for ( ; iterPending != m_PendingRecords.end(); ++iterPending) {
		if ((*iterPending)->m_strHostName == aHostName) {
			RT_WARNING_TRACE("CRtDnsManager::SyncResolve,"
				" remove pending for hostname=" << aHostName);
			pRecordNew = (*iterPending);
			m_PendingRecords.erase(iterPending);

			// TODO: If it's processing, wait util reloved.
			RT_ASSERTE(pRecordNew->m_State == CRtDnsRecord::RSV_IDLE);
			break;
		}
	}

	int nErr = -998;
	if (!pRecordNew) {
		pRecordNew = new CRtDnsRecord(aHostName
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
			, m_bIsEL98
#endif
			);
		if (!pRecordNew)
			goto fail;
	}
	
	m_PendingRecords.push_front(pRecordNew);
	nErr = DoGetHostByName_l(pRecordNew.ParaIn());
	
fail:
	Resolved_l(pRecordNew.ParaIn(), nErr, FALSE);
	if (!nErr) {
		aRecord = pRecordNew.Get();
		aRecord->AddReference();
		return RT_OK;
	}
	else {
		return RT_ERROR_NETWORK_DNS_FAILURE;
	}
}

RtResult CRtDnsManager::
FindInCache_l(CRtDnsRecord *&aRecord, const CRtString &aHostName)
{
	RT_ASSERTE(!aRecord);

	CacheRecordsType::iterator iter = m_CacheRecords.find(aHostName);
	if (iter != m_CacheRecords.end()) {
		aRecord = (*iter).second.Get();
		RT_ASSERTE(aRecord);
		RT_ASSERTE(aHostName == aRecord->m_strHostName);
		
		if (aRecord->m_State == CRtDnsRecord::RSV_SUCCESS) {
			aRecord->AddReference();
			return RT_OK;
		}
		else if (aRecord->m_State == CRtDnsRecord::RSV_FAILED) {
			aRecord = NULL;
			return RT_ERROR_NETWORK_DNS_FAILURE;
		}
		else {
			RT_ERROR_TRACE("CRtDnsManager::FindInCache_l,"
				" error state in m_CacheRecords"
				" aHostName=" << aHostName <<
				" aRecord=" << aRecord <<
				" state=" << aRecord->m_State);
			RT_ASSERTE(FALSE);
			return RT_ERROR_UNEXPECTED;
		}
	}
	return RT_ERROR_NOT_FOUND;
}

RtResult CRtDnsManager::RefreshHost(const CRtString &aHostName)
{
	RT_INFO_TRACE("CRtDnsManager::RefreshHost,"
		" aHostName=" << aHostName);

	CRtAutoPtr<CRtDnsRecord> m_pOldRecord;
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	CacheRecordsType::iterator iter = m_CacheRecords.find(aHostName);
	if (iter != m_CacheRecords.end()) {
		m_pOldRecord = (*iter).second;
		RT_ASSERTE(m_pOldRecord->m_State == CRtDnsRecord::RSV_SUCCESS || 
			m_pOldRecord->m_State == CRtDnsRecord::RSV_FAILED);
		RT_ASSERTE(m_pOldRecord->m_strHostName == aHostName);
		m_CacheRecords.erase(iter);
	}

	if (!m_pOldRecord) {
		m_pOldRecord = new CRtDnsRecord(aHostName
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
			, m_bIsEL98
#endif
			);
	}
	else {
		m_pOldRecord->m_State = CRtDnsRecord::RSV_IDLE;
	}

	int nErr = BeginResolve_l(m_pOldRecord.ParaIn());
	if (nErr) {
		Resolved_l(m_pOldRecord.ParaIn(), nErr, FALSE);
		return RT_ERROR_FAILURE;
	}
	return RT_ERROR_WOULD_BLOCK;
}

RtResult CRtDnsManager::GetLocalIps(CRtDnsRecord *&aRecord)
{
	RT_ASSERTE(!aRecord);

	char szBuf[512];
	int nErr = ::gethostname(szBuf, sizeof(szBuf));
	if (nErr != 0) {
#ifdef RT_WIN32
		errno = ::WSAGetLastError();
#endif // RT_WIN32
		RT_ERROR_TRACE("CRtDnsManager::GetLocalIps, gethostname() failed! err=" << errno);
		return RT_ERROR_FAILURE;
	}

	RtResult rv = SyncResolve(aRecord, szBuf);
	return rv;
}

int CRtDnsManager::BeginResolve_l(CRtDnsRecord *aRecord)
{
	RT_ASSERTE_RETURN(aRecord, -999);

	PendingRecordsType::iterator iterPending = m_PendingRecords.begin();
	for ( ; iterPending != m_PendingRecords.end(); ++iterPending) {
		if ((*iterPending)->m_strHostName == aRecord->m_strHostName) {
/*
			RT_WARNING_TRACE("CRtDnsManager::BeginResolve_l,"
				" pending for hostname=" << aRecord->m_strHostName);
*/
			return 0;
		}
	}

	CRtAutoPtr<CRtDnsRecord> pRecordNew = aRecord;
	m_PendingRecords.push_back(pRecordNew);

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	if(m_bIsEL98)
	{
		RtResult rv = RT_OK;
		if (!m_pThreadDNS) 
			rv = SpawnDnsThread_l();
		if (RT_SUCCEEDED(rv))
			rv = m_pThreadDNS->GetEventQueue()->PostEvent(this);
		return RT_SUCCEEDED(rv) ? 0 : -1;
	}
	int nRet = 0;
	// Note form MSDN:
	// The WSAAsyncGetHostByName function is not designed to provide parallel 
	// resolution of several names. Therefore, applications that issue several 
	// requests should not expect them to be executed concurrently.
	//
	// so we have to resolve one by one, and pend it before if resloving.
	if (m_PendingRecords.size() == 1) {
		nRet = DoAsyncHostByName_l(pRecordNew.ParaIn());
	}
	return nRet;
#else
	RtResult rv = RT_OK;
	if (!m_pThreadDNS) 
		rv = SpawnDnsThread_l();
	if (RT_SUCCEEDED(rv))
		rv = m_pThreadDNS->GetEventQueue()->PostEvent(this);
	return RT_SUCCEEDED(rv) ? 0 : -1;
#endif // RT_WIN32_ENABLE_AsyncGetHostByName
}

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
int CRtDnsManager::DoAsyncHostByName_l(CRtDnsRecord *aRecord)
{
	RT_ASSERTE(!m_bIsEL98);
	RT_ASSERTE(aRecord);
	RT_ASSERTE(aRecord->m_State == CRtDnsRecord::RSV_IDLE);
	aRecord->m_State = CRtDnsRecord::RSV_PROCESSING;

	RT_ASSERTE_RETURN(m_hwndDsnWindow, RT_ERROR_NOT_INITIALIZED);
	HANDLE hdNew = ::WSAAsyncGetHostByName(
		m_hwndDsnWindow, 
		WM_WIN32_DNS_EVENT, 
		aRecord->m_strHostName.c_str(),
		aRecord->m_szBuffer,
		sizeof(aRecord->m_szBuffer));
	if (hdNew == 0) {
		int nErr = ::WSAGetLastError();
		RT_ASSERTE(nErr != 0);
		RT_ERROR_TRACE("CRtDnsManager::DoAsyncHostByName_l, WSAAsyncGetHostByName() failed."
			" err=" << nErr);
		return nErr;
	}
	RT_ASSERTE(aRecord->m_HandleResolve == 0);
	aRecord->m_HandleResolve = hdNew;
	return 0;
}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

int CRtDnsManager::DoGetHostByName_l(CRtDnsRecord *aRecord)
{
	RT_ASSERTE(aRecord);
	RT_ASSERTE(aRecord->m_State == CRtDnsRecord::RSV_IDLE);
	aRecord->m_State = CRtDnsRecord::RSV_PROCESSING;
	::memset(aRecord->m_szBuffer, 0, sizeof(aRecord->m_szBuffer));

	// unlock the mutex because gethostbyname() will block the current thread.
	m_Mutex.UnLock();
	int nError = 0;
	struct hostent *pHeResult = NULL;
	// There will be crash if gethostbyname_r(""), strange!
	// So we have to use gethostbyname() instead gethostbyname_r().
//#ifdef RT_WIN32
#if defined (RT_WIN32) || defined (RT_MACOS)
	pHeResult = ::gethostbyname(aRecord->m_strHostName.c_str());
	if (pHeResult) {
		CopyHostent_i(aRecord, pHeResult);
	}
	else {
#ifdef RT_WIN32
		nError = ::WSAGetLastError();
#else
		nError = errno;
#endif // RT_WIN32
	}
#else 
	struct hostent *pheResultBuf = 
		reinterpret_cast<struct hostent *>(aRecord->m_szBuffer);
#ifndef RT_SOLARIS
	if (::gethostbyname_r(aRecord->m_strHostName.c_str(), 
		pheResultBuf, 
		aRecord->m_szBuffer + sizeof(hostent), 
		sizeof(aRecord->m_szBuffer) - sizeof(hostent), 
		&pheResultBuf,
		&nError) == 0)
#else
	if (::gethostbyname_r(aRecord->m_strHostName.c_str(), 
		pheResultBuf, 
		aRecord->m_szBuffer + sizeof(hostent), 
		sizeof(aRecord->m_szBuffer) - sizeof(hostent), 
		&nError) != NULL)
#endif // !RT_SOLARIS
	{
		pHeResult = pheResultBuf;
	}
#endif // RT_WIN32
	if (!pHeResult) {
		if (!nError)
#ifdef RT_MACOS
		    RT_ASSERTE(nError);
#else
			nError = EADDRNOTAVAIL;
#endif	
		RT_ERROR_TRACE("CRtDnsManager::DoGetHostByName_l, gethostbyname() failed."
			" host=" << aRecord->m_strHostName << " err=" << nError);
	}

	m_Mutex.Lock();
	return nError;
}

static inline void CopyLpstr(LPSTR aSrc, LPSTR &aDst)
{
	// need copy '\0'
	if (aSrc) {
		size_t nLen = strlen(aSrc) + 1;
		::memcpy(aDst, aSrc, nLen);
		aDst += nLen;
	}
	else {
		::memset(aDst, 0, sizeof(LPSTR));
		aDst += sizeof(LPSTR);
	}
}

static inline void Copy4Bytes(DWORD a4BytesBuf, LPSTR &aDst)
{
	::memcpy(aDst, &a4BytesBuf, 4);
	aDst += 4;
}

void CRtDnsManager::
CopyHostent_i(CRtDnsRecord *aRecord, struct hostent *aHostent)
{
	RT_ASSERTE(aRecord);
	RT_ASSERTE(aHostent);

#if 0
	// it will cause crash
	::memcpy(aRecord->m_szBuffer, aHostent, sizeof(aRecord->m_szBuffer));
#else
	struct hostent *pHeNew = reinterpret_cast<struct hostent *>(aRecord->m_szBuffer);
	LPSTR pStart = aRecord->m_szBuffer + sizeof(struct hostent);
	if (aHostent->h_name) {
		pHeNew->h_name = pStart;
		CopyLpstr(aHostent->h_name, pStart);
	}
	else {
		pHeNew->h_name = NULL;
	}
	pHeNew->h_addrtype = aHostent->h_addrtype;
	pHeNew->h_length = aHostent->h_length;
#ifdef WIN32
	if (aHostent->h_aliases) {
		pHeNew->h_aliases = reinterpret_cast<char **>(pStart);
		int nNum = 0;
		char **pAliases = aHostent->h_aliases;
		for ( ; *pAliases; ++pAliases) 
			nNum++;

		LPSTR pNumPtr = pStart;
		pStart += (nNum + 1) * 4;
		for (int i = 0; i < nNum; ++i) {
			Copy4Bytes(reinterpret_cast<DWORD>(pStart), pNumPtr);
			CopyLpstr((aHostent->h_aliases)[i], pStart);
		}
		Copy4Bytes(0, pNumPtr);
	}
	else {
		pHeNew->h_aliases = NULL;
	}
	if (aHostent->h_addr_list) {
		pHeNew->h_addr_list = reinterpret_cast<char **>(pStart);
		int nNum = 0;
		char **pAddrList = aHostent->h_addr_list;
		for ( ; *pAddrList; ++pAddrList) 
			nNum++;

		RT_ASSERTE(aHostent->h_length == 4);
		LPSTR pNumPtr = pStart;
		pStart += (nNum + 1) * 4;
		for (int i = 0; i < nNum; ++i) {
			Copy4Bytes(reinterpret_cast<DWORD>(pStart), pNumPtr);
			Copy4Bytes(
				*reinterpret_cast<DWORD*>((aHostent->h_addr_list)[i]), 
				pStart);
		}
		Copy4Bytes(0, pNumPtr);
	}
	else {
		pHeNew->h_addr_list = NULL;
	}
#endif
#endif
}

RtResult CRtDnsManager::
TryAddObserver_l(IRtObserver *aObserver, CRtThread *aThreadListener, const CRtString &aHostName)
{
	if (!aObserver)
		return RT_ERROR_INVALID_ARG;

	if (!aThreadListener) {
		aThreadListener = CRtThreadManager::Instance()->GetThread(CRtThreadManager::TT_CURRENT);
		RT_ASSERTE(aThreadListener);
	}

	ObserversType::iterator iter = m_Observers.begin();
	for ( ; iter != m_Observers.end(); ++iter) {
		if ((*iter).m_pObserver == aObserver) {
			RT_WARNING_TRACE("CRtDnsManager::TryAddObserver_l, observer already exist."
				" aObserver=" << aObserver << 
				" aThreadListener=" << aThreadListener);
			return RT_ERROR_FOUND;
		}
	}

	CObserverAndListener obsNew(this, aObserver, aThreadListener, 0, aHostName);
	m_Observers.push_back(obsNew);
	return RT_OK;
}

RtResult CRtDnsManager::
Resolved_l(CRtDnsRecord *aRecord, int aError, BOOL aCallback)
{
	RT_ASSERTE(aRecord);
	RT_ASSERTE(aRecord->m_State == CRtDnsRecord::RSV_PROCESSING);
	RT_INFO_TRACE("CRtDnsManager::Resolved_l,"
		" pRecord=" << aRecord << 
		" hostname=" << aRecord->m_strHostName << 
		" aError=" << aError);
	
	if (!aError) {
		// it's successful.
		aRecord->m_State = CRtDnsRecord::RSV_SUCCESS;
	}
	else {
		aRecord->m_State = CRtDnsRecord::RSV_FAILED;
	}
	aRecord->m_tvResolve = CRtTimeValue::GetTimeOfDay();

	m_CacheRecords[aRecord->m_strHostName] = aRecord;

	PendingRecordsType::iterator iter = std::find(
		m_PendingRecords.begin(), 
		m_PendingRecords.end(), 
		aRecord);
	if (iter != m_PendingRecords.end()) {
		m_PendingRecords.erase(iter);
	}
	else {
		RT_ERROR_TRACE("CRtDnsManager::Resolved_l, can't find pending."
			" maybe it's removed due to Sync and Aysnc resolve the same hostname."
			" hsotname" << aRecord->m_strHostName);
		RT_ASSERTE(FALSE);
	}

	if (aCallback)
		DoCallback_l(aError, aRecord->m_strHostName);
	return RT_OK;
}

RtResult CRtDnsManager::DoCallback_l(int aError, const CRtString &aHostName)
{
	if (m_Observers.empty())
		return RT_OK;
	ObserversType obvOnCall(m_Observers);

	// Fix crash bug that "aHostName" may be released in CRtDnsManager::OnTimer().
	CRtString strHostName(aHostName);

	// don't hold the mutex when doing callback 
	m_Mutex.UnLock();
	ObserversType::iterator iter = obvOnCall.begin();
	for ( ; iter != obvOnCall.end(); ++iter) {
		if ((*iter).m_strHostName != strHostName)
			continue;

		if (CRtThreadManager::IsEqualCurrentThread((*iter).m_pThreadListener->GetThreadId())) {
			IRtObserver *pObserver = (*iter).m_pObserver;
			if (pObserver) {
				// allow OnObserver() once.
				RtResult rv = CancelResolve(pObserver);
				if (RT_SUCCEEDED(rv)) {
					int nErr = aError;
					pObserver->OnObserve("DnsManager", &nErr);
				}
			}
		}
		else {
			IRtEventQueue *pEventQueue = (*iter).m_pThreadListener->GetEventQueue();
			if (pEventQueue) {
				CObserverAndListener *pEventNew = new CObserverAndListener(*iter);
				pEventNew->m_nError = aError;
				pEventQueue->PostEvent(pEventNew);
			}
		}
	}
	m_Mutex.Lock();
	return RT_OK;
}

RtResult CRtDnsManager::CancelResolve(IRtObserver *aObserver)
{
//	RT_INFO_TRACE("CRtDnsManager::CancelResolve,"
//		" aObserver=" << aObserver);

	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	ObserversType::iterator iter = m_Observers.begin();
	for ( ; iter != m_Observers.end(); ++iter) {
		if ((*iter).m_pObserver == aObserver) {
			m_Observers.erase(iter);
			return RT_OK;
		}
	}

//	RT_WARNING_TRACE("CRtDnsManager::CancelResolve,"
//		" observer not exist. aObserver=" << aObserver);
	return RT_ERROR_NOT_FOUND;
}

RtResult CRtDnsManager::Shutdown()
{
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	if(m_bIsEL98) return RT_OK;
	
	RT_ASSERTE(m_hwndDsnWindow==NULL);
#else
	if (m_pThreadDNS) {
		m_pThreadDNS->Stop();
		m_pThreadDNS = NULL;
	}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName
	m_Observers.clear();
	m_PendingRecords.clear();
	m_CacheRecords.clear();
	return RT_OK;
}

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
void CRtDnsManager::DestroyDnsWindow()
{
	if (m_hwndDsnWindow) {
		BOOL rv = ::DestroyWindow(m_hwndDsnWindow);
		RT_ASSERTE(rv);
		m_hwndDsnWindow = NULL;
		RT_INFO_TRACE("CRtDnsManager::DestroyDnsWindow");
	}
}
#endif

RtResult CRtDnsManager::CObserverAndListener::OnEventFire()
{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadListener->GetThreadId()));
	
	RtResult rv = m_pDnsManager->CancelResolve(m_pObserver);
	if (RT_SUCCEEDED(rv) && m_pObserver)
		m_pObserver->OnObserve("DnsManager", &m_nError);
	return RT_OK;
}

//#ifndef RT_WIN32_ENABLE_AsyncGetHostByName
RtResult CRtDnsManager::SpawnDnsThread_l()
{
	RT_ASSERTE(!m_pThreadDNS);

	RtResult rv = CRtThreadManager::Instance()->CreateUserTaskThread(
		m_pThreadDNS, 
		CRtThreadManager::TF_JOINABLE, 
		FALSE);
	if (RT_FAILED(rv)) {
		RT_ERROR_TRACE("CRtDnsManager::SpawnDnsThread_l, create DNS thread failed!");
	}
	else {
		RT_INFO_TRACE("CRtDnsManager::SpawnDnsThread_l, create DNS thread.");
	}
	return rv;
}
//#endif // !RT_WIN32_ENABLE_AsyncGetHostByName

#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
LRESULT CALLBACK CRtDnsManager::
DnsEventWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg) 
	{
	case WM_WIN32_DNS_EVENT:
	{
		CRtDnsManager *pDnsManager = (CRtDnsManager *)::GetWindowLong(hwnd, 0);
		RT_ASSERTE(pDnsManager);

		HANDLE hdFind = (HANDLE)wParam;
		int nError = WSAGETASYNCERROR(lParam);
		CRtMutexGuardT<MutexType> theGuard(pDnsManager->m_Mutex);
		RT_ASSERTE_RETURN(!pDnsManager->m_PendingRecords.empty(), 0);
		CRtDnsRecord *pRecord = (*(pDnsManager->m_PendingRecords.begin())).Get();
		RT_ASSERTE(pRecord->m_HandleResolve == hdFind);
		pRecord->m_HandleResolve = 0;
		pDnsManager->Resolved_l(pRecord, nError);

		while (!pDnsManager->m_PendingRecords.empty()) {
			CRtDnsRecord *pRecordNext = (*pDnsManager->m_PendingRecords.begin()).Get();
			if (pRecordNext->m_State == CRtDnsRecord::RSV_PROCESSING)
				break;

			int nErr = pDnsManager->DoAsyncHostByName_l(pRecordNext);
			if (nErr)
				pDnsManager->Resolved_l(pRecordNext, nErr);
			else
				break;
		}
		return 0;
	}

	default:
		break;
	}
	return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName

RtResult CRtDnsManager::OnEventFire()
{
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	if(!m_bIsEL98)
	{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadNetwork->GetThreadId()));
	return CreateDnsWindow();
	}
	else
	{
		RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadDNS->GetThreadId()));
		CRtMutexGuardT<MutexType> theGuard(m_Mutex);
		while (!m_PendingRecords.empty()) {
			// must use CRtAutoPtr to backup it, because DoGetHostByName_l() 
			// maybe unlock the mutex and it's may remove in other thread.
			CRtAutoPtr<CRtDnsRecord> pRecord = (*m_PendingRecords.begin());
			int nErr = DoGetHostByName_l(pRecord.ParaIn());
			Resolved_l(pRecord.ParaIn(), nErr);
		}
		return RT_OK;
	}
#else
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadDNS->GetThreadId()));
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	while (!m_PendingRecords.empty()) {
		// must use CRtAutoPtr to backup it, because DoGetHostByName_l() 
		// maybe unlock the mutex and it's may remove in other thread.
		CRtAutoPtr<CRtDnsRecord> pRecord = (*m_PendingRecords.begin());
		int nErr = DoGetHostByName_l(pRecord.ParaIn());
		Resolved_l(pRecord.ParaIn(), nErr);
	}
	return RT_OK;
#endif // RT_WIN32_ENABLE_AsyncGetHostByName
}

void CRtDnsManager::OnDestorySelf()
{
	// don't delete this due to singleton.
}

void CRtDnsManager::OnTimer(CRtTimerWrapper* aId)
{
	if (m_CacheRecords.empty()) 
		return;

	CRtTimeValue tvCurrent = CRtTimeValue::GetTimeOfDay();
	CRtTimeValue tvExpireInterval(3,0);
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	CacheRecordsType::iterator iter = m_CacheRecords.begin();
	while (iter != m_CacheRecords.end()) {
		CRtDnsRecord *pRecord = (*iter).second.Get();
		if ((pRecord->m_State == CRtDnsRecord::RSV_SUCCESS || 
			pRecord->m_State == CRtDnsRecord::RSV_FAILED) && 
			(tvCurrent - pRecord->m_tvResolve > tvExpireInterval))
		{
			CacheRecordsType::iterator iterTmp = iter++;
			m_CacheRecords.erase(iterTmp);
		}
		else {
			++iter;
		}
	}
}


//////////////////////////////////////////////////////////////////////
// class CRtDnsRecord
//////////////////////////////////////////////////////////////////////

CRtDnsRecord::CRtDnsRecord(const CRtString &aHostName
#ifdef	   RT_WIN32_ENABLE_AsyncGetHostByName
						   ,BOOL bIsWin98
#endif
						   )
	: m_strHostName(aHostName)
	, m_State(RSV_IDLE)
	, m_tvResolve(CRtTimeValue::GetTimeOfDay())
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	, m_HandleResolve(0)
	, m_bIsEL98(bIsWin98)
#endif // RT_WIN32_ENABLE_AsyncGetHostByName
{
	RT_ASSERTE(!m_strHostName.empty());
	::memset(m_szBuffer, 0, sizeof(m_szBuffer));
}

CRtDnsRecord::~CRtDnsRecord()
{
#ifdef RT_WIN32_ENABLE_AsyncGetHostByName
	if(m_bIsEL98)
		return;
	
	if (m_HandleResolve != 0 && m_State == RSV_PROCESSING) {
		::WSACancelAsyncRequest(m_HandleResolve);
		m_HandleResolve = 0;
	}
#endif // RT_WIN32_ENABLE_AsyncGetHostByName
}

